#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

/*
int MainWindow::LoadProgram(String rFileName, WordVector *rOutput)
{
    std::ifstream infile(rFileName);

    if(!infile)
    {
        return 1;
    }

    String line;

    while (std::getline(infile, line))
    {
        std::istringstream iss(line);

        int lineSize = line.size();
        if(lineSize != MAX_LINE_LENGTH)
        {
            std::cout << " lines must not be longer than 4 bytes." << std::endl;
            return 1;
        }
        else
        {
            Word temp;
            temp[0] = line.at(0);
            temp[1] = line.at(1);
            temp[2] = line.at(2);
            temp[3] = line.at(3);

            rOutput->push_back(temp);
        }
    }
    return 0;
}
*/

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    if(LogManager::getSingletonPtr() == 0)
    {
        logManager = new LogManager();
    }
    LogManager::getSingleton().Error("MainWindow", "derp");

    //OS
    if(RealMachine::getSingletonPtr() == 0)
    {
        realMachine = new RealMachine();
    }

    if(OS::getSingletonPtr() == 0)
    {
        operatingSystem = new OS();
    }



    //GUI
    ui->setupUi(this);

    ui->out->setReadOnly(true);
    ui->out->setStyleSheet("background: #2B2E36; color: #31B3D2");

    ui->in->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->in->setFocus();

    ui->mem->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->mem->verticalHeader()->setStyleSheet("QHeaderView::section {background-color: #0D2238;}");
    ui->mem->horizontalHeader()->hide();

    ui->process->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->process->horizontalHeader()->setStyleSheet("QHeaderView::section {background-color: #0D2238;}");
    ui->process->verticalHeader()->hide();

    ui->resource->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->resource->horizontalHeader()->setStyleSheet("QHeaderView::section {background-color: #0D2238;}");
    ui->resource->verticalHeader()->hide();

    ui->reg_BAR0->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_BAR1->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_IO0->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_IO1->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_IO2->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_IO3->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_PS0->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_PS1->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_MODE->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_PL0->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_PL1->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_PPR->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_SF->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_RA0->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_RA1->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_RA2->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_RA3->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_RB0->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_RB1->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_RB2->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_RB3->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_SPR0->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_SPR1->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_SR0->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_SR1->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_TIME->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_NEXT0->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_NEXT1->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_NEXT2->setStyleSheet("background: #2B2E36; color: #31B3D2");
    ui->reg_NEXT3->setStyleSheet("background: #2B2E36; color: #31B3D2");

    iostate = CONSOLE;

    runType = 0;

    rmemModel = new QStandardItemModel(MEMORY_BLOCKS * BLOCK_SIZE, 4, this);
    vmemModel = new QStandardItemModel(BLOCKS * BLOCK_SIZE, 4, this);
    smemModel = new QStandardItemModel(BLOCKS * BLOCK_SIZE, 4, this);

    processModel = new QStandardItemModel(1, 3, this);
    resourceModel = new QStandardItemModel(1, 3, this);



    {
        QStringList labels;
        for(int row = 0; row < rmemModel->rowCount(); row++)
        {
            QVariant data = rmemModel->headerData(row, Qt::Vertical);
            labels << QString::number( data.toInt() - 1, 16 );
        }
        rmemModel->setVerticalHeaderLabels(labels);
    }

    {
        QStringList labels;
        for(int row = 0; row < vmemModel->rowCount(); row++)
        {
            QVariant data = vmemModel->headerData(row, Qt::Vertical);
            labels << QString::number( data.toInt() - 1, 16 );
        }
        vmemModel->setVerticalHeaderLabels(labels);
    }

    {
        QStringList labels;
        for(int row = 0; row < smemModel->rowCount(); row++)
        {
            QVariant data = smemModel->headerData(row, Qt::Vertical);
            labels << QString::number( data.toInt() - 1, 16 );
        }
        smemModel->setVerticalHeaderLabels(labels);
    }

    PrintHelp();

    UpdateRegisterView();
    updateProcessList();
}
MainWindow::~MainWindow()
{
    Planner::getSingleton().deleteProcess(0);

    delete ui;
    delete operatingSystem;
    delete realMachine;
    delete logManager;
}

void MainWindow::on_listView_activated(const QModelIndex &index)
{

}

void MainWindow::on_send_clicked()
{
    processUserInput();
}

void MainWindow::on_in_returnPressed()
{
    processUserInput();
}

void MainWindow::processUserInput()
{
    QString message = ui->in->text();

    switch(iostate) {
    case CONSOLE: {
        if(message != "")
        {
            ui->in->clear();
            ui->out->append("> "+message);
            QStringList tokens = message.split( " " );
            if(tokens[0] == "help")
            {
                PrintHelp();
            }
            else if(tokens[0] == "show" && tokens.size() > 1)
            {
                if(tokens[1] == "v")
                {
                    CurrentMemType = 2;
                    UpdateMemView();
                }else if(tokens[1] == "r")
                {
                    CurrentMemType = 1;
                    UpdateMemView();
                }else if(tokens[1] == "s")
                {
                    CurrentMemType = 3;
                    UpdateMemView();
                }
                else
                {
                    ui->out->append("Invalid command.");
                }

            }
            else if(tokens[0] == "load" && tokens.size() > 1)
            {
                /*
                WordVector program;
                std::string utf8_location = tokens[1].toUtf8().constData();
                if(LoadProgram(utf8_location, &program))
                {
                    ui->out->append("File not found or contains more than 4 bytes per line.");
                }else
                {
                    //rm.LoadProgram(&program);
                    UpdateMemView();
                    UpdateRegisterView();
                    runType = 0;

                    ui->out->append("File loaded.");
                }
                */
                std::string utf8_location = tokens[1].toUtf8().constData();
                std::ifstream infile(utf8_location);

                if(!infile)
                {
                    ui->out->append("Can't find file:"+QString::fromStdString(utf8_location));
                }else {
                    //Resource* programUrl = new ProgramLoadingBuffer(-1, utf8_location);
                    ResourceAllocator::getSingleton().addResource(new ProgramLoadingBuffer(-1, utf8_location));
                    //ResourceAllocator::getSingleton().addResource(programUrl);
                    updateProcessList();
                    updateResourceList();
                }
            }
            else if(tokens[0] == "exit")
            {
                Resource* endJob = ResourceAllocator::getSingleton().getResource("EndJob");
                if(endJob != 0) {
                    //ResourceAllocator::getSingleton().freeResource(endJob);
                    ResourceAllocator::getSingleton().addResource(new EndJob(-1));
                }else {
                    qDebug() << "EndJob not found @ MainWindow::exit";
                }

                //exit(0);
            }
            else
            {
                ui->out->append("Invalid command.");
            }
        }
    }break;
    case READ_WORD: {
        if(message != "")
        {
            ui->in->clear();
            if(message.size() > 4)
            {
                message.resize(4);
            }
            ui->out->append(": "+message);
            Word ra;
            ra[0] = 0x00;
            ra[1] = 0x00;
            ra[2] = 0x00;
            ra[3] = 0x00;
            for(int m = 0; m < message.size(); m++)
            {
                ra[m] = message.at(m).toLatin1();
            }
            //rm.setRA(ra);
            //rm.setMODE(0x00);
            Word clear;
            clear[0] = 0x00;
            clear[1] = 0x00;
            clear[2] = 0x00;
            clear[3] = 0x00;
            //rm.setIO(clear);
            iostate = CONSOLE;
            UpdateRegisterView();
            UpdateMemView();
            if(runType == 2)
            {
                on_pushButton_2_clicked();
            }
        }
    }break;
    case READ_BLOCK: {
        if(message != "")
        {
            ui->in->clear();
            iobuffer.append(message.toStdString());
            if(iobuffer.size() > 40)
            {
                iobuffer.resize(40);
            }
            unsigned found = iobuffer.find("\\n");
            if(iobuffer.size() == 40 || found!=std::string::npos)
            {
                if(found!=std::string::npos)
                {
                    iobuffer.resize(found);
                }
                ui->out->append(": "+QString::fromStdString(iobuffer));
                /*
                int addr = vmLoc*BLOCK_SIZE;
                while(iobuffer.size() > 0)
                {
                    if(iobuffer.size() >= 4)
                    {
                        Word temp;
                        temp[0] = iobuffer.at(0);
                        temp[1] = iobuffer.at(1);
                        temp[2] = iobuffer.at(2);
                        temp[3] = iobuffer.at(3);
                        rm.setVirtualMem(addr, temp);
                        addr++;
                        iobuffer.erase(0, 4);
                    }
                    else
                    {
                        Word temp;
                        temp[0] = 0x00;
                        temp[1] = 0x00;
                        temp[2] = 0x00;
                        temp[3] = 0x00;
                        for(int m = 0; m < iobuffer.size(); m++)
                        {
                            temp[m] = iobuffer.at(m);
                        }
                        rm.setVirtualMem(addr, temp);
                        addr++;
                        iobuffer.erase(0, iobuffer.size());
                    }
                }
                iobuffer.clear();
                rm.setMODE(0x00);
                Word clear;
                clear[0] = 0x00;
                clear[1] = 0x00;
                clear[2] = 0x00;
                clear[3] = 0x00;
                rm.setIO(clear);
                iostate = CONSOLE;
                UpdateRegisterView();
                UpdateMemView();
                if(runType == 2)
                {
                    on_pushButton_2_clicked();
                }
                */
            }
        }
    }break;
    case STOP: {
        //
    }break;
    default: {
        //
    }break;
    }
}

void MainWindow::PrintHelp(void)
{
    ui->out->append("> This is the command list:");
    ui->out->append("> show v (displays the memory of virtual machine)");
    ui->out->append("> show r (displays the memory of real machine)");
    ui->out->append("> show s (displays shared memory)");
    ui->out->append("> load filename (loads file 'filename' to memory)");
    ui->out->append("> exit (closes the application)");
}

void MainWindow::ShowVirtualMem()
{
    for(int row = 0; row < BLOCKS * BLOCK_SIZE; row++)
    {
        for(int col = 0; col < MAX_LINE_LENGTH; col++)
        {
            QModelIndex index = vmemModel->index(row, col, QModelIndex());
            //Byte derp = rm.getVirtualMem(row)[col];
            Byte derp;
            QString herp(derp);
            vmemModel->setData(index, herp);
        }
    }
    ui->mem->setModel(vmemModel);
}
void MainWindow::ShowRealMem()
{
    for(int row = 0; row < MEMORY_BLOCKS * BLOCK_SIZE; row++)
    {
        for(int col = 0; col < MAX_LINE_LENGTH; col++)
        {
            QModelIndex index = rmemModel->index(row, col, QModelIndex());
            //rmemModel->setData(index, QString((rm.getRealMem(row))[col]));
            rmemModel->setData(index, 0);
        }
    }
    ui->mem->setModel(rmemModel);
}
void MainWindow::ShowSharedMem()
{
    //int address = rm.getBAR()[0]*BLOCK_SIZE;
    for(int row = 0; row < BLOCKS * BLOCK_SIZE; row++)
    {
        for(int col = 0; col < MAX_LINE_LENGTH; col++)
        {
            QModelIndex index = smemModel->index(row, col, QModelIndex());
            //smemModel->setData(index, QString((rm.getRealMem(address))[col]));
            smemModel->setData(index, 0);
        }
        //address++;
    }
    ui->mem->setModel(smemModel);
}
void MainWindow::UpdateMemView()
{
    if(CurrentMemType == 1)
    {
        ShowRealMem();
    }
    else if(CurrentMemType == 2)
    {
        ShowVirtualMem();
    }
    else if(CurrentMemType == 3)
    {
        ShowSharedMem();
    }
}
void MainWindow::UpdateRegisterView()
{
    /*
    ui->reg_RA0->setText(QString::fromStdString(Hex(rm.getRA()[0])));
    ui->reg_RA1->setText(QString::fromStdString(Hex(rm.getRA()[1])));
    ui->reg_RA2->setText(QString::fromStdString(Hex(rm.getRA()[2])));
    ui->reg_RA3->setText(QString::fromStdString(Hex(rm.getRA()[3])));

    ui->reg_RB0->setText(QString::fromStdString(Hex(rm.getRB()[0])));
    ui->reg_RB1->setText(QString::fromStdString(Hex(rm.getRB()[1])));
    ui->reg_RB2->setText(QString::fromStdString(Hex(rm.getRB()[2])));
    ui->reg_RB3->setText(QString::fromStdString(Hex(rm.getRB()[3])));

    ui->reg_SR0->setText(QString::fromStdString(Hex(rm.getSR()[0])));
    ui->reg_SR1->setText(QString::fromStdString(Hex(rm.getSR()[1])));

    ui->reg_TIME->setText(QString::fromStdString(Hex(rm.getTIME())));

    ui->reg_IO0->setText(QString::fromStdString(Hex(rm.getIO()[0])));
    ui->reg_IO1->setText(QString::fromStdString(Hex(rm.getIO()[1])));
    ui->reg_IO2->setText(QString::fromStdString(Hex(rm.getIO()[2])));
    ui->reg_IO3->setText(QString::fromStdString(Hex(rm.getIO()[3])));

    ui->reg_PPR->setText(QString::fromStdString(Hex(rm.getPPR())));

    ui->reg_SPR0->setText(QString::fromStdString(Hex(rm.getSPR()[0])));
    ui->reg_SPR1->setText(QString::fromStdString(Hex(rm.getSPR()[1])));

    ui->reg_MODE->setText(QString::fromStdString(Hex(rm.getMODE())));

    ui->reg_PL0->setText(QString::fromStdString(Hex(rm.getPL()[0])));
    ui->reg_PL1->setText(QString::fromStdString(Hex(rm.getPL()[1])));

    ui->reg_BAR0->setText(QString::fromStdString(Hex(rm.getBAR()[0])));
    ui->reg_BAR1->setText(QString::fromStdString(Hex(rm.getBAR()[1])));

    ui->reg_PS0->setText(QString::fromStdString(Hex(rm.getPS()[0])));
    ui->reg_PS1->setText(QString::fromStdString(Hex(rm.getPS()[1])));

    ui->reg_SF->setText(QString::fromStdString(Hex(rm.getSF())));

    ui->reg_NEXT0->setText(QString(rm.getNEXT()[0]));
    ui->reg_NEXT1->setText(QString(rm.getNEXT()[1]));
    ui->reg_NEXT2->setText(QString(rm.getNEXT()[2]));
    ui->reg_NEXT3->setText(QString(rm.getNEXT()[3]));
    */
}
void MainWindow::Step(void)
{
    qDebug() << "step @ MainWindow::step";
    RealMachine::getSingleton().Run();
    /*
    if(rm.getMODE() == 0)
    {
        rm.Step();
        if(rm.getMODE() != 0)
        {
            Step();
        }
    }
    else
    {
        Word io = rm.getIO();
        switch(io[0])
        {
        case 'R':{
            switch(io[1])
            {
                case 'D':{
                    vmLoc = io[3];
                    iostate = READ_BLOCK;
                    UpdateRegisterView();
                    UpdateMemView();
                    ui->in->setFocus();
                }break;
                case 'E':{
                    iostate = READ_WORD;
                    UpdateRegisterView();
                    UpdateMemView();
                    ui->in->setFocus();
                }break;
            }
        }break;
        case 'W':{
            switch(io[1])
            {
                case 'R':{
                    Word ra = rm.getRA();
                    QString o;
                    o.push_back((ra[0]));
                    o.push_back((ra[1]));
                    o.push_back((ra[2]));
                    o.push_back((ra[3]));
                    ui->out->append(o);
                    rm.setMODE(0x00);
                    Word clear;
                    clear[0] = 0x00;
                    clear[1] = 0x00;
                    clear[2] = 0x00;
                    clear[3] = 0x00;
                    rm.setIO(clear);
                }break;
                case 'T':{
                    vmLoc = io[3];
                    qDebug() << vmLoc;
                    int address = vmLoc * BLOCK_SIZE;
                    qDebug() << address;
                    iobuffer.clear();

                    while(iobuffer.size() < 40)
                    {
                        Word temp = rm.getVirtualMem(address);
                        iobuffer.push_back((temp[0]));
                        iobuffer.push_back((temp[1]));
                        iobuffer.push_back((temp[2]));
                        iobuffer.push_back((temp[3]));
                        address++;
                    }
                    unsigned found = iobuffer.find("\\n");
                    if(found!=std::string::npos)
                    {
                        iobuffer.resize(found);
                    }
                    ui->out->append(QString::fromStdString(iobuffer));
                    iobuffer.clear();
                    rm.setMODE(0x00);
                    Word clear;
                    clear[0] = 0x00;
                    clear[1] = 0x00;
                    clear[2] = 0x00;
                    clear[3] = 0x00;
                    rm.setIO(clear);
                }break;
            }
        }break;
        default:{

        }break;
        }
    }
    */
    updateProcessList();
    updateResourceList();
}
void MainWindow::updateProcessList() {
    processListBase processess = Planner::getSingleton().processList;
    processModel = new QStandardItemModel(processess.size(), 4, this);

    processModel->setHeaderData( 0, Qt::Horizontal, QObject::tr("ID") );
    processModel->setHeaderData( 1, Qt::Horizontal, QObject::tr("Step") );
    processModel->setHeaderData( 2, Qt::Horizontal, QObject::tr("State") );
    processModel->setHeaderData( 3, Qt::Horizontal, QObject::tr("Name") );


    int i = 0;
    for(processListIterator it = processess.begin(); it != processess.end(); ++it, i++)
    {
        QModelIndex index = processModel->index(i, 0, QModelIndex());
        processModel->setData(index, (*it)->id);
        index = processModel->index(i, 1, QModelIndex());
        processModel->setData(index, (*it)->currentStep);
        index = processModel->index(i, 2, QModelIndex());
        String stateStr = "";
        switch((*it)->state) {
            case RUN:{
                stateStr = "RUN";
            }break;
            case READY:{
                stateStr = "READY";
            }break;
            case READY_STOPPED:{
                stateStr = "READY_S";
            }break;
            case BLOCKED:{
                stateStr = "BLOCKED";
            }break;
            case BLOCKED_STOPPED:{
                stateStr = "BLOCKED_S";
            }break;
        }
        processModel->setData(index, QString::fromStdString(stateStr));
        index = processModel->index(i, 3, QModelIndex());
        processModel->setData(index, QString::fromStdString((*it)->name));
    }
    ui->process->setModel(processModel);
    ui->process->setColumnWidth(0, 40);
    ui->process->setColumnWidth(1, 60);
    ui->process->setColumnWidth(2, 80);
}

void MainWindow::updateResourceList() {
    resourceListBase resources = ResourceAllocator::getSingleton().resourceList;
    resourceModel = new QStandardItemModel(resources.size(), 4, this);

    resourceModel->setHeaderData( 0, Qt::Horizontal, QObject::tr("ID") );
    resourceModel->setHeaderData( 1, Qt::Horizontal, QObject::tr("FreeElem") );
    resourceModel->setHeaderData( 2, Qt::Horizontal, QObject::tr("ParentID") );
    resourceModel->setHeaderData( 3, Qt::Horizontal, QObject::tr("Name") );

    int i = 0;
    for(resourceListIterator it = resources.begin(); it != resources.end(); ++it, i++)
    {
        QModelIndex index = resourceModel->index(i, 0, QModelIndex());
        resourceModel->setData(index, (*it)->resID);
        index = resourceModel->index(i, 1, QModelIndex());
        resourceModel->setData(index, (*it)->getFreeElements());
        index = resourceModel->index(i, 2, QModelIndex());
        Process* parentProcess = Planner::getSingleton().findProcess((*it)->pID);
        String parentProcessName = "";
        if(parentProcess != 0) {
            parentProcessName = parentProcess->name;
        }else {
            parentProcessName = "NULL";
        }
        resourceModel->setData(index, QString::fromStdString(parentProcessName));
        index = resourceModel->index(i, 3, QModelIndex());
        resourceModel->setData(index, QString::fromStdString((*it)->name));
    }
    ui->resource->setModel(resourceModel);
    ui->resource->setColumnWidth(0, 40);
    ui->resource->setColumnWidth(1, 60);
    ui->resource->setColumnWidth(2, 80);
}

void MainWindow::on_pushButton_clicked()
{
    if(runType == 0)
    {
        runType = 1;
    }
    Step();
    UpdateRegisterView();
    UpdateMemView();
}

void MainWindow::on_pushButton_2_clicked()
{
    if(runType == 0 || runType == 1)
    {
        runType = 2;
    }
    /*
    while(rm.getPPR()!= 0x05 && rm.getMODE() == 0)
    {
        Step();
    }
    */
    UpdateRegisterView();
    UpdateMemView();
}
